package com.example.doudou.demos.aop;

/**
 * @Author fangtao
 * @Date 2025 02 14 09 43
 * @Description
 **/
import cn.hutool.json.JSONUtil;
import com.example.doudou.demos.aspect.AuditLog;
import com.example.doudou.demos.pojo.UserLogRecord;
import com.example.doudou.demos.service.UserLogRecordService;
import com.example.doudou.demos.utils.BusinessException;
import com.example.doudou.demos.utils.CreateUUID;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.HashMap;

/**
 * 日志记录切面
 */
@Slf4j
@Aspect
@Component
public class AuditAspect {
    @Autowired
    private UserLogRecordService userLogRecordService;

    @Pointcut(value = "@annotation(com.example.doudou.demos.aspect.AuditLog)")
    public void logMethodPointCut() {
    }

    @Around("logMethodPointCut()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
        UserLogRecord userLogRecordDO = bulidRequestParams(point);
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        AuditLog annotation = method.getAnnotation(AuditLog.class);
        long currentTime =  System.currentTimeMillis();
        //先执行业务
        try {
            Object result = point.proceed();
            if (result != null) {
                if (annotation.isRecordResult()) {
                    userLogRecordDO.setResultMsg(JSONUtil.toJsonStr(result));
                }
            }
            userLogRecordDO.setResultCode("200");
            return result;
        } catch (BusinessException e) {
            userLogRecordDO.setResultCode(e.getErrorCode());
            userLogRecordDO.setResultMsg(e.getMessage());
            throw e;
        } catch (Exception e) {
            userLogRecordDO.setResultCode("0000");
            userLogRecordDO.setResultMsg("后端未知异常");
            throw e;
        } finally {
            long endTime =  System.currentTimeMillis();
            //操作日志入库
            try {
                userLogRecordDO.setId(CreateUUID.creatUUID());
                userLogRecordDO.setTimeConsum(endTime - currentTime);
                userLogRecordDO.setCreateTime(LocalDateTime.now());
                userLogRecordService.save(userLogRecordDO);
                log.info("记录用户操作：{},消耗的时间为{}ms", JSONUtil.toJsonStr(userLogRecordDO), endTime - currentTime);
            } catch (Exception e) {
                log.warn("记录用户操作异常：{}", e.getMessage());
            }

        }
    }


    /**
     * 记录方法和方法入参
     * @param point
     * @return
     */
    protected UserLogRecord bulidRequestParams(ProceedingJoinPoint point) {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        AuditLog annotation = method.getAnnotation(AuditLog.class);

        UserLogRecord userLogRecordDO = new UserLogRecord();
        userLogRecordDO.setAction(annotation.action());
        userLogRecordDO.setType(annotation.type().name());
        userLogRecordDO.setMethodName(method.getDeclaringClass().getSimpleName() + "." + method.getName());
        if (annotation.isRecordParam()) {
            try {
                // 处理入参
                Parameter[] parameters = methodSignature.getMethod().getParameters();
                HashMap<String, Object> paramMap = new HashMap<>();
                Object[] args = point.getArgs();
                for (int i = 0; i < parameters.length; i++) {
                    AuditLog auditLog = parameters[i].getAnnotation(AuditLog.class);
                    if (auditLog != null) {
                        continue;
                    }

                    Class<?> type = parameters[i].getType();
                    if (ServletResponse.class.isAssignableFrom(type) || ServletRequest.class.isAssignableFrom(type)) {
                        continue;
                    }
                    String name = parameters[i].getName();
                    paramMap.put(name, args[i]);
                }

                userLogRecordDO.setMethodParams(JSONUtil.toJsonStr(paramMap));
                return userLogRecordDO;
            } catch (Exception e) {
                log.warn("构建入参异常：{}", e.getMessage());
            }
        }
        return userLogRecordDO;
    }
}
